﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace Lilium.Collections
{
	public class CyclicQueue<T>: IEnumerable<T>
	{
		public CyclicQueue(int length)
		{
			if (length < 0)
				throw new InvalidOperationException("Queue length can't be less then zero");

			m_Queue = new T[length];
		}

		public void Enqueue(T item)
		{   
			var length = Queue.Length ;
			if (length == 0)
				return;
			
			Queue[Tail] = item;
			Tail = (Tail + 1) % length;
			if (Count == length)
				Head = (Head + 1) % length;
			else
				Count++;
		}

		public T Dequeue()
		{
			if (Count == 0)
				throw new InvalidOperationException("Queue is empty");

			T local = Queue[Head];
			Queue[Head] = default(T);
			Head = (Head + 1) % Queue.Length;
			return local;
		}


		/// <summary>
		/// Returns enumerator which enumerates from queue tail to the head.
		/// </summary>
		/// <returns></returns>
		public IEnumerator<T> GetReversedEnumerator()
		{
			var length = Count;
			if (length == 0)
				yield break;

			if (Tail > Head)
				for (int i = Tail - 1; i >= Head; i--)
					yield return Queue[i];
			else
			{
				for (int i = Tail - 1; i >= 0; i--)
					yield return Queue[i];
				for (int i = length - 1; i >= Head; i--)
					yield return Queue[i];
			}
		}

		public bool TryPeekLast(out T item)
		{
			if (Count != 0)
			{
				item = Queue[Tail != 0 ? Tail - 1 : Queue.Length - 1];
				return true;
			}
			else
			{
				item = default(T);
				return false;
			}
		}

		#region public int Count

		private int m_Count;

		public int Count
		{
			get
			{
				return m_Count;
			}
			private set
			{
				m_Count = value;
			}
		}

		#endregion

		#region IEnumerable<T> Members

		public IEnumerator<T> GetEnumerator()
		{
			var length = Queue.Length;
			if (length == 0)
				yield break;

			if (Tail > Head)
				for (int i = Head; i < Tail; i++)
					yield return Queue[i];
			else
			{
				for (int i = Head; i < length; i++)
					yield return Queue[i];
				for (int i = 0; i < Tail - 1; i++)
					yield return Queue[i];
			}
		}

		#endregion

		#region IEnumerable Members

		IEnumerator IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}

		#endregion

		#region private T[] Queue

		private readonly T[] m_Queue;

		private T[] Queue
		{
			get
			{
				return m_Queue;
			}
		}

		#endregion

		#region private int Head

		private int m_Head;

		private int Head
		{
			get
			{
				return m_Head;
			}
			set
			{
				m_Head = value;
			}
		}

		#endregion

		#region private int Tail

		private int m_Tail;

		private int Tail
		{
			get
			{
				return m_Tail;
			}
			set
			{
				m_Tail = value;
			}
		}

		#endregion
	}
}